#include <compilation/task/PhiInsertionTask.h>


namespace compilation
{


PhiInsertionAgenda::~PhiInsertionAgenda(void)
{
  for (std::vector<LabelInstruction*>::iterator labelIter = m_labels.begin();
       labelIter != m_labels.end(); ++labelIter)
  {
    if ((*labelIter)->mark)
    {
      delete (PhiInsertionTask::LabelResult*) (*labelIter)->mark;
      (*labelIter)->mark = nullptr;
    }
  }
  m_labels.clear();
}


void
PhiInsertionAgenda::markInstructionWith(
    VirtualInstruction& instruction, VirtualTask& vtTask)
{
  if (instruction.hasType<LabelInstruction>())
  {
    const PhiInsertionTask& task = vtTask.as<PhiInsertionTask>();

    if (!instruction.mark)
    {
      instruction.checkType<LabelInstruction>();
      instruction.mark = new PhiInsertionTask::LabelResult();
    }

    PhiInsertionTask::LabelResult& result =
        instruction.getMarkAs<PhiInsertionTask::LabelResult>();

    if (!result.hasMark())
    {
      m_labels.push_back(instruction_cast<TLabel>(&instruction));
      result.mark();
      result.setScope(task.getScope());
    }
  }
}


void PhiInsertionExpressionHandler<TLocalVariable>::operator ()(
    VirtualExpression* expr,
    PhiInsertionTask& task,
    PhiInsertionAgenda& continuations,
    WorkListReusability& reuse)
{
  base_handler::operator ()(expr,task,continuations,reuse);

  if (task.isLValue())
  {
    if (task.getModifiedVariables().find(expr) ==
        task.getModifiedVariables().end())
    {
      DEBUG_EXPRESSION_VISITOR
          << "add modified variable: "
          << expression_cast<TLocalVariable>(expr)->name()
          << std::endl;
      task.addModifiedVariables(expr);
    }

    if (task.hasDominationFrontier())
    {
      for (GotoInstruction* it : *task.getDominationFrontier())
      {
        LabelInstruction& label =
            instruction_cast<TLabel>(*(it->getSNextInstruction()));

        DEBUG_EXPRESSION_VISITOR
            << "on domination frontier: "
            << label.getRegistrationIndex()
            << std::endl;

        if (!label.mark)
        {
          label.mark = new PhiInsertionTask::LabelResult();
          DEBUG_EXPRESSION_VISITOR
              << "  add new mark"
              << std::endl;
        }
        else
        {
          DEBUG_EXPRESSION_VISITOR
              << "  has already a mark"
              << std::endl;
        }

        PhiInsertionTask::LabelResult& result =
            label.getMarkAs<PhiInsertionTask::LabelResult>();

        PhiLabelResult::Map::iterator const& found = result.map().find(expr);

        if (found == result.map().end())
        {
          std::string const& name = expression_cast<TLocalVariable>(expr)->name();
          Scope where;
          int index;
          if (label.getScope()->find(name, index, where) !=
              Scope::FindResult::FRNotFound)
          {
            DEBUG_EXPRESSION_VISITOR
                << "  expression is inserted in the label"
                << std::endl;

            std::pair<VirtualExpression*,
                std::pair<GotoInstruction*, GotoInstruction*> > insert;
            insert.first = expr;
            insert.second.first = nullptr;
            insert.second.second = nullptr;

            result.map().insert(insert);
          }
          else
          {
            DEBUG_EXPRESSION_VISITOR
                << "  expression not visible for the label"
                << std::endl;
          }
        }
        else
        {
          DEBUG_EXPRESSION_VISITOR
              << "  expression already in the label"
              << std::endl;
        }
      }
    }
  }
}


void PhiInsertionExpressionHandler<TAssign>::operator ()(
    VirtualExpression* expr,
    PhiInsertionTask& task,
    PhiInsertionAgenda& continuations,
    WorkListReusability& reuse)
{
  base_handler::operator ()(expr,task,continuations,reuse);

  task.setLValueStatement(true);
  continuations.handleExpression(
        expression_cast<TAssign>(expr)->getLValue(),
        task, continuations, reuse);
  task.setLValueStatement(false);
}


void PhiInsertionInstructionHandler<TExitBlock>::operator ()(
    VirtualInstruction* instr,
    PhiInsertionTask& task,
    PhiInsertionAgenda& continuations,
    WorkListReusability& reuse)
{
  (void) instruction_cast<TExitBlock>(instr);

//  // XXX: Some variable are missed if you add these lines.
//  LocalVariableExpression lastExpr(std::string(), 0, task.getScope());

//  PhiInsertionTask::ModifiedVariables::iterator last =
//      task.getModifiedVariables().upper_bound(&lastExpr);

//  if (debug_instruction_visitor)
//  {
//    for (auto it = last; it != task.getModifiedVariables().end(); ++it)
//      DEBUG_INSTRUCTION_VISITOR
//          << "remove " << expression_cast<TLocalVariable>(*it)->name()
//          << std::endl;
//  }

//  task.eraseModifiedVariables(last, task.getModifiedVariables().end()); // $1

  task.getScope().pop();
  base_handler::operator ()(instr,task,continuations,reuse);
}


void PhiInsertionInstructionHandler<TEnterBlock>::operator ()(
    VirtualInstruction* instr,
    PhiInsertionTask& task,
    PhiInsertionAgenda& continuations,
    WorkListReusability& reuse)
{
  (void) instruction_cast<TEnterBlock>(instr);

  //    task.setScope(gt->scope());
  task.getScope().push();

  base_handler::operator ()(instr,task,continuations,reuse);
}


void PhiInsertionInstructionHandler<TLabel>::operator ()(
    VirtualInstruction* instr,
    PhiInsertionTask& task,
    PhiInsertionAgenda& continuations,
    WorkListReusability& reuse)
{
  LabelInstruction* gt = instruction_cast<TLabel>(instr);

  task.setDominationFrontier(&gt->getDominationFrontier());

  base_handler::operator ()(instr,task,continuations,reuse);
}


void PhiInsertionInstructionHandler<TGoto>::operator ()(
    VirtualInstruction* instr,
    PhiInsertionTask& task,
    PhiInsertionAgenda& continuations,
    WorkListReusability& reuse)
{
  GotoInstruction* gt = instruction_cast<TGoto>(instr);

  if (gt->isContext(GotoInstruction::CAfterIfThen) ||
      gt->isContext(GotoInstruction::CAfterIfElse))
  {
    DEBUG_INSTRUCTION_VISITOR
        << (gt->isContext(GotoInstruction::CAfterIfThen)
            ? "is after then"
            : "is after else")
        << std::endl;

    task.setDominationFrontier(&gt->getDominationFrontier());
    if (instr->getSPreviousInstruction() &&
        instr->getSPreviousInstruction()->type() == TIf)
    {
      if (debug_instruction_visitor)
      { std::cout << "  is after if" << std::endl; }

      for (GotoInstruction* it : *task.getDominationFrontier())
      {
        LabelInstruction& label =
            *instruction_cast<TLabel>(it->getSNextInstruction());

        if (!label.mark)
        {
          label.mark = new PhiInsertionTask::LabelResult();

          DEBUG_INSTRUCTION_VISITOR
              << "add new LabelResult mark to label at "
              << label.getRegistrationIndex()
              << std::endl;
        }
        else
        {
          DEBUG_INSTRUCTION_VISITOR
              << "label at "
              << label.getRegistrationIndex()
              << " has already a LabelResult mark"
              << std::endl;
        }

        label.getMarkAs<PhiInsertionTask::LabelResult>().variablesToAdd().
            insert(task.getModifiedVariables().begin(),
                   task.getModifiedVariables().end());

        if (debug_instruction_visitor)
        {
          for (VirtualExpression const* expr : task.getModifiedVariables())
          {
            LocalVariableExpression const* var =
                expression_cast<TLocalVariable>(expr);

            DEBUG_INSTRUCTION_VISITOR
                << "add modified expression "
                << var->name() << " from "
                << var->getGlobalIndex()
                << " to label "
                << label.getRegistrationIndex()
                << std::endl;
          }
        }
      }
    }
  }

  base_handler::operator ()(instr,task,continuations,reuse);
}


void PhiInsertionInstructionHandler<TExpression>::operator ()(
    VirtualInstruction* instr,
    PhiInsertionTask& task,
    PhiInsertionAgenda& continuations,
    WorkListReusability& reuse)
{
  VirtualExpression* expr = instruction_cast<TExpression>(instr)->getExpression();
  if (expr) continuations.handleExpression(expr, task, continuations, reuse);

  base_handler::operator ()(instr,task,continuations,reuse);
}


bool PhiInsertionPropagator<TGoto>::operator ()(
    VirtualInstruction* instr,
    PhiInsertionTask& task,
    PhiInsertionAgenda& continuations,
    WorkListReusability& reuse)
{
  DEBUG_INSTRUCTION_VISITOR << "propagate on goto" << std::endl;
  GotoInstruction* gt = instruction_cast<TGoto>(instr);

  if (gt->isContext(GotoInstruction::CLoop) ||
      gt->isContext(GotoInstruction::CBeforeLabel))
  {
    DEBUG_INSTRUCTION_VISITOR
        << "  context: "
        << (gt->isContext(GotoInstruction::CLoop)
            ? "loop" : "before label")
        << std::endl;

    if (gt->getSNextInstruction() &&
        gt->getSNextInstruction()->type() == TLabel)
    {
      DEBUG_INSTRUCTION_VISITOR
          << "  next expression is a label"
          << std::endl;

      LabelInstruction& label =
          *instruction_cast<TLabel>(gt->getSNextInstruction());

      if (!label.mark)
        label.mark = new PhiInsertionTask::LabelResult();

      bool hasFoundFrontier = false;
      if (task.getDominationFrontier())
      {
        DEBUG_INSTRUCTION_VISITOR
            << "  task has domination frontier"
            << std::endl;

        for (GotoInstruction* gt : *task.getDominationFrontier())
        {
          if (gt->getSNextInstruction() == &label) // $1
          {
            DEBUG_INSTRUCTION_VISITOR
                << "  found corresponding label in domination frontier at "
                << label.getRegistrationIndex()
                << std::endl;

            PhiInsertionTask::LabelResult& result =
                label.getMarkAs<PhiInsertionTask::LabelResult>();

            LocalVariableExpression
                afterLast(std::string(),
                          task.getScope().count(),
                          task.getScope());

            std::set<VirtualExpression*, PhiIsBefore> const& modified =
                task.getModifiedVariables();

            for (VirtualExpression* iter : modified)
            {
              // XXX: Doesn't work if you use this condition.
//              if (reinterpret_cast<PhiInsertionTask::IsBefore*>(0x0)
//                  ->operator()(iter, &afterLast)) // $2

              std::string const& name = expression_cast<TLocalVariable>(iter)->name();
              Scope where;
              int index;
              if (label.getScope()->find(name, index, where) !=
                  Scope::FindResult::FRNotFound) // $2
              {
                PhiInsertionTask::LabelResult::iterator found =
                    result.map().find(iter);

                if (found != result.map().end()) // $3
                {
                  /**/ if (!found->second.first)
                  {
                    DEBUG_INSTRUCTION_VISITOR
                        << "add first reference to "
                        << expression_cast<TLocalVariable>(iter)->name()
                        << " at "
                        << label.getRegistrationIndex()
                        << std::endl;
                    found->second.first = gt;
                  }
                  if (!found->second.second && found->second.first != gt)
                  {
                    DEBUG_INSTRUCTION_VISITOR
                        << "add second reference to "
                        << expression_cast<TLocalVariable>(iter)->name()
                        << " at "
                        << label.getRegistrationIndex()
                        << std::endl;
                    found->second.second = gt;
                  }
                  else { assert(found->second.first == gt); }
                }
                else
                {
                  DEBUG_INSTRUCTION_VISITOR
                      << expression_cast<TLocalVariable>(iter)->name()
                      << " not modified here"
                      << std::endl;
                }
              }
            }
            hasFoundFrontier = true;
          }
        }
      }

      if (!label.getMarkAs<PhiInsertionTask::LabelResult>().hasMark())  // $4
      {
        task.clearInstruction();
        task.setInstruction(*gt->getSNextInstruction());
        reuse.setReuse();
      }

      /**/ if (hasFoundFrontier)
        task.setModifiedVariables(
              label.getMarkAs<PhiInsertionTask::LabelResult>().variablesToAdd());
      else if (label.mark)
      {
        PhiInsertionTask::LabelResult& result =
            label.getMarkAs<PhiInsertionTask::LabelResult>();

        task.addModifiedVariables(
              result.variablesToAdd().begin(),
              result.variablesToAdd().end());
      }

      return true;
    }
  }

  if (gt->isContext(GotoInstruction::CLoop) ||
      gt->isContext(GotoInstruction::CBeforeLabel) ||
      gt->isContext(GotoInstruction::CUndefined))
    reuse.setSorted();

  return base_propagator::operator ()(instr,task,continuations,reuse);
}


} // compilation
